Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: QuickDraw GX Programmer's Overview / Part 2 - The QuickDraw GX Programming Cookbook
Chapter 6 - Handling Graphics


Editing Shape Geometries

This programming recipe adds a new feature to the application created in the previous two recipes. With the code from this recipe, you can allow users to edit the geometric points of a previously created path, as shown in Figure 6-3.

Figure 6-3 Editing a path geometry

When the user presses the mouse button, the code in this recipe determines if the mouse was pressed on a geometry control handle. If so, the code tracks the mouse movement, providing feedback as the user drags the mouse by constantly erasing and redrawing the path shape.

If the place where the user clicks the mouse isn't on a geometry control handle, the code in the recipe uses the code from the previous recipes to allow the user create a new path or select a previously existing path.

Overview of Recipe Steps

The steps in this recipe show you how to:

    1. Determine if the user is editing a path
    2. Determine whether the user pressed on a geometry control handle
    3. Provide feedback as the user drags the mouse
    4. Create path shapes for feedback
    5. Redraw path shapes as the user drags the mouse
    6. Edit the selected path shape to reflect the new geometric point

You need to follow all of the steps of this recipe to implement editing of
path shapes.

Functions Used in This Recipe

QuickDraw GX functions used in this recipe:
GXHitTestShape"Shape Objects"
QuickDraw GX Objects
GXSetShapeHitTest"Transform Objects"
QuickDraw GX Objects
GXGetViewPortMouse"QuickDraw GX and the
Macintosh Environment"
QuickDraw GX Environment and Utilities
GXSetShapePoints"Geometric Shapes"
QuickDraw GX Graphics
GXDrawShape"Shape Objects"
QuickDraw GX Objects
GXCopyToShape"Shape Objects"
QuickDraw GX Objects
GXDisposeShape"Shape Objects"
QuickDraw GX Objects

Standard Macintosh functions used in this recipe:
StillDown"Event Manager"
Macintosh Toolbox Essentials

This recipe gives a brief description of these functions; you can find complete reference information for these functions in the Inside Macintosh suite of books.

This recipe also uses a function from a QuickDraw GX library:
SetShapeFastXorTransertransferMode library

Recipe Step Descriptions

In this section, each step is described individually.

  1. Determine if the user is editing a path

    In Step 1 of the previous recipe, you added new code to your MyContentClick function to determine if the user was selecting a path and, if so, to handle the path selection. In this recipe, you change the implementa-
    tion of that function again--this time to determine if the user is pressing
    on a geometry control handle. If so, you need to allow the user to drag
    the control handle around and redraw the path shape to provide dragging feedback. First, you should add these lines of code to your MyContentClick sample function:

    handled = MyHandleEditPath(&localPoint);

    if (!handled) 
    handled = MyHandleSelectPath(&localPoint);

    if (!handled)
    MyHandleCreatePath(&localPoint);

    The MyHandleEditPath sample function determines whether a geometry control handle was hit and, if so, it provides feedback as the user drags
    the mouse.

    Here is the flow of control for the MyHandleEditPath sample function:

    boolean MyHandleEditPath(gxPoint *hitPoint)
    {
    int whichControl;

        if (gCurrent->selectedPath != nil) 
    if (gCurrent->pointCount == 0)
    if (MyUserPressedControl(hitPoint, &whichControl))
    {
    MyPathDrag(hitPoint, whichControl+1);
    return(true);
    }

        return false;
    }
  2. Determine whether the user pressed on a geometry control handle

    You can use the function GXHitTestShape to determine whether the user pressed the mouse button down on a geometry control handle. The MyUserPressedControl sample function calls the GXHitTestShape function once for each geometry control handle until it determines that one of them was hit.

    boolean MyUserPressedControl(gxPoint *hitPoint, 
    int *whichControl)
    {
    gxHitTestInfo result;
    short index;

        for (index = 0; index < kNumOfControls; index++) {
    if (gCurrent->controls[index] != nil) {
    GXHitTestShape(gCurrent->controls[index],
    hitPoint, &result);
    if ((result.what & gxBoundsPart) != 0) {
    *whichControl = index;
    return true;
    }
    }
    }

        return false;
    }

    If the user hit the bounds part (that is, within the bounding rectangle) of one of the geometry control handles, this function returns true as the function result and returns the index (1 through 4) of the geometry control that
    was hit.

    You can use the GXSetShapeHitTest function to indicate to QuickDraw GX that it should hit-test only against the bounds parts of the geometry control handles. To do this, you need to add this line of code to your MyCreateControlHandle function (defined on page 188):

    GXSetShapeHitTest(*theControl, gxBoundsPart, ff(1));
  3. Provide feedback as the user drags the mouse

    If the user pressed the mouse button down on a geometry control handle, you want to allow him or her to edit the geometry of the path by dragging the mouse. The MyPathDrag sample function provides the appropriate feedback. Here is the flow of control for this function:

    void MyPathDrag (gxPoint *originalPoint, int whichControl)
    {
    /* Declare local variables -- see Steps 4 and 5. */

        MyErasePathControlHandles();

        /* Create path shapes for feedback -- see Step 4. */

        /* Prepare for feedback -- see step 5. */

        do 
    /* Redraw paths as user drags mouse -- see Step 5. */
    } while (StillDown());

        /* Edit selected path -- see Step 6. */
    }

    This function erases the geometry control handles, and then creates two path shapes to use for feedback. While the mouse is still down, this function edits the two feedback paths, and alternately draws and erases them to provide feedback while the user drags the mouse.

    The next three steps--Steps 4 through 6--show how to implement the parts of this function.

  4. Create path shapes for feedback

    The MyPathDrag function uses two path shapes to provide feedback as the user drags the mouse:

    gxShape oldPath = nil;
    gxShape newPath = nil;

    The oldPath path shape represents the previous position of the path and the newPath path shape represents the new position of the path as the user drags the mouse. Initially, both of these paths are copies of the path that the user selected:

    oldPath = GXCopyToShape(nil, gCurrent->selectedPath);
    SetShapeFastXorTransfer(oldPath &gBlack, &gWhite);
    newPath = GXCopyToShape(nil, oldPath);

    Notice that the transfer mode of these paths is set using the SetShapeFastXorTransfer library function. This exclusive-OR transfer
    mode makes it easy to erase and draw the paths fast, which is just what
    you need when providing user feedback.

  5. Redraw path shapes as the user drags the mouse

    As the user drags the mouse, you need to erase the previous version of
    the path, create a new version based on the new location of the mouse,
    and draw the new version. To begin with, you need variables to store the previous and new positions of the mouse:

    gxPoint oldPoint;
    gxPoint newPoint;

    Initially, the previous position of the mouse is the same as the point the user originally pressed the mouse button on:

    oldPoint.x = originalPoint->x;
    oldPoint.y = originalPoint->y;

    You can use the following do loop to provide feedback as the user drags
    the mouse:

    do {
    GXGetViewPortMouse(gCurrent->viewPort, &newPoint);

        if (MyMouseMoved(newPoint, oldPoint))  {
    GXSetShapePoints(newPath, /* shape to edit */
    whichControl, /* which point */
    1, /* how many points */
    &newPoint); /* new point */

            GXDrawShape(oldPath); /* erase old path */
    GXDrawShape(newPath); /* draw new path */

            GXCopyToShape(oldPath, newPath);
    oldPoint = newPoint;
    }
    } while (StillDown());

    This do loop calls the GXGetViewPortMouse function to determine the current position of the mouse in local (view port) coordinates and then calls the MyMouseMoved function, which is defined as

    boolean MyMouseMoved(gxPoint newPoint, gxPoint oldPoint) {
    return ((newPoint.x != oldPoint.x) ||
    (newPoint.y != oldPoint.y));
    }

    If the user has moved the mouse since the last time through the loop, the code in the do loop needs to erase the old path and draw a new path reflecting the new position of the geometry point.

    The call to the GXSetShapePoints function edits the new path shape to reflect the new position of the mouse. This function replaces one of the geometric points in the new path shape. The whichControl parameter specifies which geometric point is replaced--in this case, the geometric point corresponding to the geometry control handle that the user originally pressed the mouse on. The newPoint parameter specifies the new value of the geometry point--in this case, the current position of the mouse.

    After editing the new path shape, the sample code erases the path in the previous position and draws the new path. Finally, the new path becomes the old path and the new point becomes the old point for the next pass through the do loop. When the user releases the mouse button, the do loop finishes executing and you can dispose of the path shapes used for feedback:

    GXDisposeShape(oldPath);
    GXDisposeShape(newPath);
  6. Edit the selected path shape to reflect the new geometric point

    Finally, your MyPathDrag function needs to do is edit the geometry of the real path--the one contained in the window picture--to reflect the new position of the geometry control handle.

    You can use the GXSetShapePoints function to edit the geometry of the
    path shape:

    GXSetShapePoints(gCurrent-selectedPath, 
    whichControl,
    1,
    &newPoint);

    At this point, you also need to dispose of the shape representing the old geometry control handle:

    GXDisposeShape(gCurrent->controls[whichControl-1]);

    Now you can create a new geometry control handle at the new position:

    MyCreateControlHandle(&gCurrent->controls[whichControl-1], 
    &newPoint);

    To update your window, you can call the MyDrawWindow function, which is defined on page 191.

Related Recipes

The previous two recipes, "Creating Geometric Shapes" on page 179 and "Selecting Shapes," on page 193 show how to allow the user to create and select path shapes. You must complete the steps of those recipes before using this recipe.

The next recipe, "Editing Shape Transforms," shows how to provide a different kind of control handle--a transform control handle. These control handles allow the user to edit the transform of the shape, rather than the geometry of the shape.

The recipes in Chapter 4, "Using the QuickDraw GX Environment," show you how to initialize QuickDraw GX and set up the QuickDraw GX debugging facilities. You should read the recipes in that chapter before using any recipes in this chapter.

The recipes in Chapter 5, "Using Macintosh Windows," show you how to create Macintosh windows, attach QuickDraw GX view ports to them, and implement zooming, resizing, and scrolling. You need to be familiar with the information in that chapter before you can display QuickDraw GX graphics in a Macintosh window.

The recipes in Chapter 7, "Handling Typography," show you how to create and manipulate typographic, rather than graphics, shapes.

The recipes in Chapter 8, "Printing," show you how to send your graphics and typographic shapes to a printer.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help